﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")]
	public abstract class IRType : IComparable<IRType>, IEquatable<IRType>
	{
		private static int s_typeidcounter = 0;

		private readonly int m_typeid;

		private readonly string m_fullname; //the full name of the type

		//in all names we assume any generic type names have been replaced with the appropriate instantiations
		private readonly string m_namespace; //the namespace the type lives + any enclosing types
		private readonly string m_basename; //this is the simple name of the type

		private readonly IRTypeDescription m_typespec;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{ 
			Contract.Invariant(!String.IsNullOrEmpty(this.m_fullname));
			Contract.Invariant(!String.IsNullOrEmpty(this.m_basename));
			Contract.Invariant(this.m_typespec != null);
		}

		/// <summary>
		/// Construct a type given the namespace (null if default namespace), enclosing types (null if no enclosing types), and base type name.
		/// </summary>
		protected IRType(string tnamespace, string tbasename, TypeQualifiers tqual)
		{
			Contract.Requires(!String.IsNullOrEmpty(tbasename));

			this.m_typeid = s_typeidcounter++;

			String nss = (!String.IsNullOrEmpty(tnamespace)) ? (tnamespace + ".") : "";
			this.m_fullname = nss + tbasename;

			this.m_namespace = tnamespace;
			this.m_basename = tbasename;

			this.m_typespec = new IRTypeDescription(this, tqual);
		}

		/// <summary>
		/// Builtin types often want to know about specific special fields so invoke this after loading in the fields
		/// to allow for whatever extraction is required.
		/// </summary>
		public abstract void ProcessBuiltinInfoAsNeeded();

		/// <summary>
		/// Get the type but remove all scoping information.
		/// </summary>
		public String GetUnscopedType()
		{
			Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<String>()));

			return this.m_basename; 
		}

		/// <summary>
		/// Get the name of this type as a string.
		/// </summary>
		public String TypeName 
		{ 
			get 
			{
				Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<String>()));

				return this.m_fullname; 
			} 
		}

		/// <summary>
		/// Get the namespace part of this typename as a string.
		/// </summary>
		public String NameSpaceScope
		{ get { return this.m_namespace; } }

		/// <summary>
		/// Get the basename part of this typename as a string.
		/// </summary>
		public String BaseTypeName
		{
			get
			{
				Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<String>()));

				return this.m_basename;
			}
		}

		public override String ToString()
		{ return this.m_fullname; }

		public IRTypeDescription TypeDescription 
		{ 
			get 
			{
				Contract.Ensures(Contract.Result<IRTypeDescription>() != null);

				return this.m_typespec; 
			} 
		}

		public int CompareTo(IRType other)
		{ return this.m_typeid - other.m_typeid; }

		public override int GetHashCode()
		{ return this.m_typeid; }

		public override bool Equals(object obj)
		{ return (obj is IRType) && this.Equals((IRType)obj); }

		public bool Equals(IRType other)
		{ return this == other; }
	}
}
